#!/usr/bin/env python

# Entertainer backend - This is a server side of the Entertainer media center.
# Copyright (C) 2007 Lauri Taimila
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"
__version__= "0.1"

import os
import sys

# Configuration
from utils.configuration import Configuration

# Entertainer backend core
from backend.core.message import Message
from backend.core.message_bus import MessageBus
from backend.core.message_handler import MessageHandler
from backend.core.message_scheduler import MessageScheduler
from backend.core.connection_server import ConnectionServer
from backend.core.client_connection import ClientConnection
from backend.core.message_type_priority import MessageType, MessagePriority

# Entertainer backend components
from backend.components.notifier.notify import Notify
from backend.components.feeds.feed_manager import FeedManager
from backend.components.tvguide.guide_updater import GuideUpdater
from backend.components.weather.weather_manager import WeatherManager
from backend.components.mediacache.media_cache_manager import MediaCacheManager

# Logging system
import logging, logging.handlers

PREFERENCES_CONF = """\
[Recording]

[General]
show_effects = True
start_in_fullscreen = False
theme = Black
backend_port = 45054
history_size = 8
display_notifications = True
transition_effect = Slide

[DVB]
"""
    
CONTENT_CONF = """\
[Images]
folders = 
display_hidden_folders = false

[Weather]
location = EFTU
fetch_interval = 60

[Music]
folders = 
download_lyrics = False
download_album_art = True

[Videos]
folders = 
download_metadata = True

[RSS]
feeds = http://www.clutter-project.org/blog/?feed=rss2
fetch_interval = 60
"""

class BackendServer:
    """
    Entertainer backend server.
    
    This is a backend of Entertainer media center. Backend is responsible of
    many vital things like updateing media library cache, recording scheduled
    TV-shows, fetching RSS-feeds etc.
    """

    def __init__(self):
        """
        Initialize backend
        
        This method creates and initializes all backend components.
        """
        self.configuration = None     # Configuration object
        self.message_bus = None       # MessageBus object - This is the heart of the backend.
        self.logger = None            # Logger object - used to log all debug, error and info messages
        self.notifier = None          # Notify object - used to display desktop notifications
        self.connection_server = None # Connection server - Thread that listens incoming socket connections
        self.content_config = None    # content.conf - Configuration file (ConfigParser obj)
        self.preferences = None       # preferences.conf - Configuration file (ConfigParser obj)
        self.scheduler = None         # Message scheduler
        self.feed_manager = None      # Feed Manager
        self.guide_updater = None     # Guide updater
        self.media_manager = None     # Media manager (Keeps cache up-to-date)
        
        if not os.path.exists(os.path.expanduser("~/.entertainer")):
            self.create_configuration()
        
        # The order of the initialize method calls is significant! Don't change
        # the order unless you know what your doing!
        self.initialize_logging()  
        self.initialize_messaging_system()
        self.initialize_configuration()
        self.initialize_notifier()
        self.initialize_feed_manager()
        self.initialize_weather_manager()
        self.initialize_media_cache_manager()
        self.initialize_guide_updater()
        self.initialize_connection_server()
        self.initialize_scheduler()
        
    def create_configuration(self):
        """
        Create a configuration directory and default config files. This method
        is called if ~/.entertainer directory doesn't exists.
        """
        try:
            # Create all directories
            os.makedirs(os.path.expanduser("~/.entertainer/themes"))
            os.makedirs(os.path.expanduser("~/.entertainer/cache/album_art/small"))
            os.makedirs(os.path.expanduser("~/.entertainer/cache/movie_art"))
            os.makedirs(os.path.expanduser("~/.entertainer/cache/thumbnails/video"))
            os.makedirs(os.path.expanduser("~/.entertainer/cache/thumbnails/image"))
            os.makedirs(os.path.expanduser("~/.entertainer/cache/thumbnails/recording"))
            
            # Create default preferences.conf file
            fp = open(os.path.expanduser("~/.entertainer/preferences.conf"), 'w')
            fp.write(PREFERENCES_CONF)
            fp.close()
            
            # Create default content.conf file
            fc = open(os.path.expanduser("~/.entertainer/content.conf"), 'w')
            fc.write(CONTENT_CONF)
            fc.close()
        except:
            print "Couldn't create default configuration. Execution aborted..."
            sys.exit(1)
        
    def initialize_logging(self):
        """Initializes logging system to use rotating log file."""              
        logfile = os.path.expanduser('~/.entertainer/backend.log')
        log_format = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
        file_handler = logging.handlers.RotatingFileHandler(logfile, 'r+', 128000)
        file_handler.setFormatter(log_format)
        self.logger = logging.getLogger('') 
        self.logger.setLevel(logging.DEBUG)
        self.logger.addHandler(file_handler)
        self.logger.debug("Logging system intialized successfully")
    
    def initialize_messaging_system(self):
        """Initialize backend's messaging system."""
        self.message_bus = MessageBus(self.logger)
        
    def initialize_configuration(self):
        """Initialize configuration"""
        try:
            self.configuration = Configuration()
            cfg_dict = { MessageType.CONTENT_CONF_UPDATED : MessagePriority.VERY_HIGH, 
                         MessageType.PREFERENCES_CONF_UPDATED : MessagePriority.VERY_HIGH }
            self.message_bus.registerMessageHandler(self.configuration, cfg_dict)
            self.logger.debug("Configuration intialized successfully")
        except:
            print "Couldn't read configuration file! Execution aborted..."
            sys.exit(1)
            
    def initialize_notifier(self):
        """Initializes notification system"""
        self.notifier = Notify(self.logger, self.configuration)
        self.notifier.connectNotificationServer()
        notifier_dict = { MessageType.RECORDING_STARTED : MessagePriority.VERY_LOW,
                          MessageType.RECORDING_STOPPED : MessagePriority.VERY_LOW,
                          MessageType.RECORDING_CONFLICT : MessagePriority.VERY_LOW }
        self.message_bus.registerMessageHandler(self.notifier, notifier_dict)
        self.logger.debug("Notification system intialized successfully")
    
    def initialize_connection_server(self):
        """Initialize connection server."""
        try:
            self.connection_server = ConnectionServer(self.logger, 
                                                      self.configuration.get_port(), 
                                                      self.message_bus)
            self.connection_server.start() # Start listening incoming connections
        except:
            self.logger.error("ConnectionServer initialization failed!")
            print "Error occured. Please see '~/.entertainer/backend.log' for more information."
            print "This is probably not a fatal error. Just wait one minute and try again."
            print "Execution aborted!"
            sys.exit(1)
    
    def initialize_scheduler(self):
        """Initialize message scheduler."""
        self.scheduler = MessageScheduler(self.message_bus)
        self.scheduler.addMessage(Message(MessageType.UPDATE_FEEDS), 
                                    60 * self.configuration.get_feed_fetch_interval())
        self.logger.debug("Message scheduler intialized successfully")
    
    def initialize_feed_manager(self):
        """Initialize feed manager."""
        self.feed_manager = FeedManager(self.message_bus, self.logger, self.configuration)
        feed_mgr_dict = { MessageType.UPDATE_FEEDS : MessagePriority.HIGH }
        self.message_bus.registerMessageHandler(self.feed_manager, feed_mgr_dict)
        self.logger.debug("Feed Manager intialized successfully")
        
    def initialize_weather_manager(self):
        """Initialize feed manager."""
        self.weather_manager = WeatherManager(self.message_bus, self.logger, self.configuration)
        weather_mgr_dict = { MessageType.UPDATE_WEATHER : MessagePriority.HIGH }
        self.message_bus.registerMessageHandler(self.weather_manager, weather_mgr_dict)
        self.logger.debug("Weather Manager intialized successfully")
        
    def initialize_guide_updater(self):
        self.guide_updater = GuideUpdater(self.message_bus, self.logger)
        guide_dict = { MessageType.UPDATE_GUIDE : MessagePriority.VERY_HIGH }
        self.message_bus.registerMessageHandler(self.guide_updater, guide_dict)
        self.logger.debug("Guide Updater intialized successfully")
     
    def initialize_media_cache_manager(self):
        self.media_manager = MediaCacheManager(self.logger, self.configuration)
        media_dict = { MessageType.CONTENT_CONF_UPDATED : MessagePriority.VERY_LOW,
                       MessageType.REBUILD_IMAGE_CACHE : MessagePriority.HIGH,
                       MessageType.REBUILD_MUSIC_CACHE : MessagePriority.HIGH,
                       MessageType.REBUILD_VIDEO_CACHE : MessagePriority.HIGH }
        self.message_bus.registerMessageHandler(self.media_manager, media_dict)
        self.logger.debug("Media Manager intialized successfully")
        
    def quitBackend(self):
        #FIXME: Do here all shutdown cleaning
        self.message_bus.unregisterAllMessageHandlers()
